Skip to content

feat(agc): emulate undithered alpha-to-coverage - #3228

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
GustavoGamarra95:feat/agc-alpha-to-coverage
Oct 11, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
GustavoGamarra95:feat/agc-alpha-to-coverage

Conversation

@GustavoGamarra95

Copy link
Copy Markdown
Contributor

What

God of War Sons of Sparta (PPSA28997) stopped a few minutes into play with alpha-to-coverage is unsupported (register 0x2dc = 0x0000aa01).

  • Decode. DB_ALPHA_TO_MASK = 0xaa01 is accepted: the enable bit with the four quad offsets at 2 and no OFFSET_ROUND, the undithered setting radeonsi programs. The following still throw:
    • any other offsets, or OFFSET_ROUND;
    • an MRT0 that is missing, swizzled, packed or exported without a floating-point alpha;
    • a pixel shader that exports a sample mask.
  • Shader. Draws are single-sampled, so the pixel stage gets an alphaToCoverage flag. Such a variant declares SampleMask and starts with every sample covered. At its MRT0 export it writes 0 when the alpha is below one half or NaN, and all ones otherwise. It runs with late fragment tests. The shader keeps running after the export, so later stores still happen, as they do with a zero coverage mask on the GPU.
  • Shader identity. The flag is part of the static shader identity (CacheKey) and of the request serialization, now version 16.
  • Vulkan's alphaToCoverageEnable is not used: the NVIDIA driver 615.78.08 dithers single-sample coverage. An alpha of 0.25 writes half the pixels of an RTX 3050 Laptop target.

Tested

  • The rule was measured on a Radeon 660M (gfx1035) with RADV and RADV_DEBUG=noatocdithering, which programs DB_ALPHA_TO_MASK = 0xaa01. I compared the GPU's own Vulkan alpha-to-coverage with this emulation for 16 alpha values: 0, -0, 0.25, the two floats below one half, one half, the two floats above it, 0.75, 1, 2, -1, both infinities and both NaNs. All 16 matched on the 660M, and the emulation gave the same result on the RTX 3050. The rows are pinned in tests/execution/AlphaToCoverage.cpp. They are not measured on gfx1013, as TechnicalDebt.md says.
  • Arch Linux, kernel 7.2.9, GCC 16.2.1, Release, on main d31b57a: ctest 752/753 passed on the RTX 3050. The failure is agc_driver_dcc_keys_first_write, which fails on main on this GPU too. Each commit builds and passes the tests it touches (agc_shader_memory, agc_driver_graphics, agc_driver_dual_source_blend).
  • PPSA28997 now plays past that point, through several levels, with feat(libkernel): implement _umtx_op wait and wake operations #1482 and feat(libkernel): deliver sceKernelRaiseException on Linux #1431 applied for Linux.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

🤖 Generated with Claude Code

…erage

A pixel shader whose stage info asks for alpha-to-coverage declares SampleMask, starts with every sample covered and, at its MRT0 export, clears the mask when the alpha is below one half or NaN, running with late fragment tests. The shader keeps running, as a zero coverage mask does on the GPU. The flag is part of the static shader identity and of the request serialization, now version 16.
DB_ALPHA_TO_MASK 0xaa01, the enable bit with the four offsets at 2 and no OFFSET_ROUND, reaches the pixel stage, which keeps a pixel when its MRT0 alpha is at least one half. Dithered or rounded settings, and an MRT0 that is missing, swizzled, packed or exported without a floating-point alpha, still throw. God of War Sons of Sparta (PPSA28997) stopped on it a few minutes into play.
The expected rows were measured with Vulkan alpha-to-coverage on a Radeon 660M (gfx1035) under RADV_DEBUG=noatocdithering, which programs DB_ALPHA_TO_MASK 0xaa01: an alpha of at least one half, 2 and +inf keep the pixel; below one half, -0, negatives, -inf and NaN drop it.
@github-actions github-actions Bot added the intake paused Closed while PR intake is limited to authors with at least 10 PRs merged into main label Oct 11, 2026
@github-actions

Copy link
Copy Markdown

Thank you for your interest in the project! We are sorry, but we cannot accept more PRs while we work through the existing backlog. This PR has been automatically closed and labeled "intake paused". This is not a judgment of the quality of your changes.

@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
#2169 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp L600-602, L989-991

@github-actions github-actions Bot added build shader core/shader docs agc libSceAgc* labels Oct 11, 2026
@reyani-dev

Copy link
Copy Markdown
Contributor

Windows run, since the PR has only Linux results. Windows 11 (10.0.26200), WinLibs GCC 15.2.0 r7, Release, NVIDIA RTX 4070 Laptop GPU (driver 616.92), head 50e84c7c merged onto current main, with VK_LOADER_DRIVERS_DISABLE=*dzn* (this machine's Dozen ICD crashes on its own, #3255).

  • It builds cleanly. The only two warnings are nodiscard ones in AttributeInstructions.cpp and tests/Graphics.cpp that main already has; the header changes just cause those files to recompile.
  • agc_driver_alpha_to_coverage (the pinned 16-value rows), agc_shader_memory, agc_driver_graphics and agc_driver_dual_source_blend pass.
  • ctest -R "^agc_|video_out|^shader|recompiler": 358/358 pass. agc_driver_dcc_keys_first_write, which fails on your RTX 3050, passes on this GPU both with and without the change.

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked on current main: scoped build and ctest (agc_driver_graphics, agc_shader_memory, agc_driver_prepared_graphics, agc_driver_alpha_to_coverage) pass. I also swapped the emulation for Vulkan's alphaToCoverageEnable on RADV (Radeon 660M, RADV_DEBUG=noatocdithering) and the new test passes all rows, so the 0.5/NaN rule matches the hardware path; with dithering on it differs, as the description says.

Notes, not blocking:

Merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build docs shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants